package cm.tl.pms.util;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDFontDescriptor;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import static cm.tl.pms.common.constant.ConstantConfig.*;

@Slf4j
public class PdfHighlightUtil {


    public static List<PageResult> processPdf(String pdfPath, List<String> contents, int dpi, String dataSourceId) {
        List<PageResult> results = new ArrayList<>();
        PDDocument document;
        try {
            if (pdfPath.startsWith("/")) {
                pdfPath = getUploadFilePreUrl() + pdfPath;
                log.info("_pdfPath={}", pdfPath);
            }
            if (pdfPath.toLowerCase().startsWith("http")) {
                URL url = new URL(pdfPath);
                URLConnection connection = url.openConnection();
                connection.setConnectTimeout(50000); // 设置连接超时时间
                InputStream inputStream = connection.getInputStream();
                document = PDDocument.load(inputStream);
            } else {
                document = PDDocument.load(new File(pdfPath));
            }

            PDFRenderer renderer = new PDFRenderer(document);
            log.info("page={}", document.getNumberOfPages());
            for (int pageIndex = 0; pageIndex < document.getNumberOfPages(); pageIndex++) {
                PDPage page = document.getPage(pageIndex);
                PDRectangle mediaBox = page.getMediaBox();
                int pageNumber = pageIndex + 1;

                // 渲染PDF页面为图片
                float scale = dpi / 72.0f;
                BufferedImage image = renderer.renderImage(pageIndex, scale, ImageType.RGB);
                if (image == null) {
                    log.info("Failed to load image {}", pageIndex);
                }
                String path = getUploadFilePrefix() + getUploadFilePath() + "/";
                File upload = new File(path);
                if (!upload.exists() && !upload.mkdirs()) {
                    log.info("Failed to create directory: " + path);
                }
                if (StringUtils.isBlank(dataSourceId)) {
                    dataSourceId = System.currentTimeMillis() + "";
                }

                String imagePath = path + dataSourceId + "_img" + pageIndex + ".png";
                File output = new File(imagePath);
                log.info("{}", imagePath);
                if (!output.exists()) {
                    ImageIO.write(image, "PNG", output);
                }
                // 提取文本位置信息
                List<CharInfo> charInfos = extractTextPositions(document, pageIndex, mediaBox.getHeight(), scale);

                // 查找高亮区域
                List<ContentArea> contentAreas = new ArrayList<>();
                for (String content : contents) {
                    List<HighlightArea> areas = new ArrayList<>();
//                    areas.addAll(findHighlightAreas(charInfos, content,pageNumber));

                    for (String splitText : content.split("[；。]")) {
                        areas.addAll(findHighlightAreas(charInfos, splitText, pageNumber));
                    }

                    ContentArea ca = new ContentArea();
                    ca.setContent(content);
                    ca.setAreas(areas);
                    contentAreas.add(ca);
                }

                imagePath = imagePath.replace(getUploadFilePrefix(), getUploadFilePreUrl());
                // 构建返回结果
                PageResult result = new PageResult();
                result.setPageNumber(pageNumber);
                result.setImageWidth(image.getWidth());
                result.setImageHeight(image.getHeight());
                result.setImagePath(imagePath);
                result.setContentAreas(contentAreas);

                results.add(result);
            }
        } catch (Exception e) {
            log.info("processPdf:", e);
        }
        return results;
    }

    private static List<CharInfo> extractTextPositions(PDDocument document, int pageIndex, float pageHeight, float scale) throws IOException {
        TextPositionExtractor stripper = new TextPositionExtractor();
        stripper.setStartPage(pageIndex + 1);
        stripper.setEndPage(pageIndex + 1);
        stripper.getText(document);

        List<CharInfo> charInfos = new ArrayList<>();
        int lineNumber = 1;
        float lineHeight = 0;
        float lineY = 0;
        float preY = -1;
        for (TextPosition tp : stripper.getTextPositions()) {
            float h = tp.getHeight();
            PDFont font = tp.getFont();
            float fontSize = tp.getFontSizeInPt();
            PDFontDescriptor descriptor = font.getFontDescriptor();
            if (descriptor != null) {
                // 计算实际ascent和descent
                float ascent = (descriptor.getAscent() / 1000) * fontSize;
                float descent = Math.abs((descriptor.getDescent() / 1000) * fontSize);
                h = ascent + descent;
            }

            String character = tp.getUnicode();
            float x = tp.getXDirAdj() * scale;
            float y = tp.getYDirAdj() * scale;
            float width = tp.getWidthDirAdj() * scale;
            float height = h * scale;
            float y1 = tp.getY();

            if (preY != -1 && Math.abs(y1 - preY) > 10) {
                lineNumber++;
                lineY = preY;
            }
            preY = y1;

            if (lineY > 0) {
                lineHeight = Math.max(Math.abs(lineY - y1), h) * scale;
            } else {
                lineHeight = height;
            }

            charInfos.add(new CharInfo(character, x, y, width, height, lineHeight, lineNumber));
        }
        return charInfos;
    }

    private static List<HighlightArea> findHighlightAreas(List<CharInfo> charInfos, String targetText, Integer pageNumber) {
        StringBuilder pageText = new StringBuilder();
        for (CharInfo ci : charInfos) {
            pageText.append(ci.character);
        }
        String fullText = pageText.toString();
        log.info("fullText=={}", fullText);

        List<HighlightArea> areas = new ArrayList<>();

        int index = 0;

        while ((index = fullText.indexOf(targetText, index)) != -1) {
            int endIndex = index + targetText.length();
            if (endIndex > charInfos.size()) break;

            List<CharInfo> matched = charInfos.subList(index, endIndex);
            float minX = Float.MAX_VALUE;
            float minY = Float.MAX_VALUE;
            float maxX = Float.MIN_VALUE;
            float maxY = Float.MIN_VALUE;
            float height = 0;
            float lineHeight = 0;
            int line = 0;
            int lineNumber = 0;

            for (CharInfo ci : matched) {
                lineNumber = ci.getLineNumber();
                height = ci.getHeight();
                lineHeight = ci.getLineHeight();
                if (line == 0) {
                    line = lineNumber;
                } else if (line != lineNumber) {
                    HighlightArea area = new HighlightArea();
                    area.setX(minX);
                    area.setY(minY - height);
                    area.setWidth(maxX - minX);
                    area.setHeight(lineHeight);
                    area.setPageNumber(pageNumber);
                    area.setLineNumber(line);
                    areas.add(area);

                    minX = Float.MAX_VALUE;
                    minY = Float.MAX_VALUE;
                    maxX = Float.MIN_VALUE;
                    maxY = Float.MIN_VALUE;
                    line = lineNumber;
                }

                minX = Math.min(minX, ci.x);
                minY = Math.min(minY, ci.y);
                maxX = Math.max(maxX, ci.x + ci.width);
                maxY = Math.max(maxY, ci.y - ci.height);
            }

            HighlightArea area = new HighlightArea();
            area.setX(minX);
            area.setY(minY - height);
            area.setWidth(maxX - minX);
            area.setHeight(lineHeight);
            area.setPageNumber(pageNumber);
            area.setLineNumber(lineNumber);
            areas.add(area);

            index = endIndex;
        }
        return areas;
    }


    // 自定义TextPosition提取器
    static class TextPositionExtractor extends PDFTextStripper {
        private final List<TextPosition> textPositions = new ArrayList<>();

        public TextPositionExtractor() throws IOException {
            super();
            super.setSortByPosition(true);
        }

        @Override
        protected void writeString(String text, List<TextPosition> textPositions) {
            this.textPositions.addAll(textPositions);
        }

        public List<TextPosition> getTextPositions() {
            return textPositions;
        }
    }

    // 坐标DTO
    @Data
    static class CharInfo {
        String character;
        float x;
        float y;
        float width;
        float height;
        float lineHeight;
        int lineNumber;

        public CharInfo(String character, float x, float y, float width, float height, float lineHeight, int lineNumber) {
            this.character = character;
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
            this.lineHeight = lineHeight;
            this.lineNumber = lineNumber;
        }
    }

    @Data
    static class HighlightArea {
        private float x;
        private float y;
        private float width;
        private float height;
        int lineNumber;
        int pageNumber;

    }

    @Data
    static class ContentArea {
        private String content;
        private List<HighlightArea> areas;
    }

    @Data
    static class PageResult {
        private int pageNumber;
        private int imageWidth;
        private int imageHeight;
        private String imagePath;
        List<ContentArea> contentAreas;

    }


    public static void main(String[] args) throws IOException {
        String url = "https://www.citic-prudential.com.cn/uploadfiles/2024/08/20240819161651703.pdf";
//        String fileName = "D:\\123tc/pdf-high2.pdf";
        String fileName = "D:\\123tc/2.pdf";
//        DownloadUtil.downloadUrl(url, fileName);

        String ss = "服务直达、诉求直通的高效服务。";
        List<String> s = new ArrayList<>();
        s.add(ss);

        processPdf(fileName, s, 300, "");

    }
}